/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package edu.ieslaferreria.creditsintesis.logicgame;


import com.jme.math.Vector3f;
import com.jme.scene.Node;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Esta clase determina el tamaña del mapa y intentamos con esto crear tantas
 * divisiones como necesitemos.
 * nos servirá a la hora de detectar que objetos hay en cada región, sobretodo
 * a la hora de colisionar, de esta forma no tenemos que mirar siempre todos
 * los objetos, sino que miraremos los que están dentro de una región
 * También nos permite identificar no solo los objetos que son susceptibles de
 * cambiar la posición, sino los objetos que tienen una posición fija, un cambio
 * de estado etc.
 *
 * @author Josep
 */
public class RegionMap {


    Vector3f vectorLong;
    int divisions;
    //private HashMap<Vector3f,Integer> vectorHashMap = new HashMap();
    private ConcurrentHashMap<Vector3f, ConcurrentHashMap<ObjectWithStates, ObjectWithStates>> vectorHashMap = new ConcurrentHashMap();



// This method create a Mapa with a Vector3f and the number of regions.
    public RegionMap(Vector3f vL1, int divisions){
        this.vectorLong=vL1;
        this.divisions=divisions;

    }
    
    public Iterator<ObjectWithStates> getObjectsWithStatesNearTo(Vector3f vector){
        IteratorObjectWithStates iterator = new IteratorObjectWithStates();

        iterator.addIterator(vectorHashMap.get(getRegionVector3f(vector)).values().iterator());

        return iterator;
    }

    public Iterator<ObjectWithStates> getLogicObjectsWithStatesNearTo(Vector3f vector){
        IteratorLogicObjectWithStates iterator = new IteratorLogicObjectWithStates();

        iterator.addIterator(vectorHashMap.get(getRegionVector3f(vector)).values().iterator());

        return iterator;
    }

    public Iterator<Node> getGraphicObjectsWithStatesNearTo(Vector3f vector){
        IteratorNodeObjectWithStates iterator = new IteratorNodeObjectWithStates();

        iterator.addIterator(vectorHashMap.get(getRegionVector3f(vector)).values().iterator());

        return iterator;
    }

    
    public Iterator<ObjectWithStates> getAllObjectsWithStates(){
        IteratorObjectWithStates iterator = new IteratorObjectWithStates();

        int cont=0;
        while(cont<vectorHashMap.size()){
            iterator.addIterator(vectorHashMap.get(getRegionVector3f(new Vector3f(0f,0f,0f))).values().iterator());
            cont++;
        }
        return iterator;
    }



    public Vector3f getRegionVector3f(Vector3f vector){
        Vector3f ret=null;
        int posX = (int) (vector.getX()/(this.vectorLong.getX()/this.divisions));
        int posY = (int) (vector.getY()/(this.vectorLong.getY()/this.divisions));
        int posZ = (int) (vector.getZ()/(this.vectorLong.getZ()/this.divisions));

        ret = new Vector3f(posX, posY, posZ);


        return ret;
    }

    public void add(ObjectWithStates ob){
        Vector3f position = ob.getWorldTranslation();
        add(position, ob);
    }

    public void add(Vector3f position, ObjectWithStates ob){
        ConcurrentHashMap<ObjectWithStates, ObjectWithStates> set;
        Vector3f region = getRegionVector3f(position);
        if(this.vectorHashMap.containsKey(region)){
            set = this.vectorHashMap.get(region);
        }else{
            set = new ConcurrentHashMap();
            this.vectorHashMap.put(region, set);
        }
        set.put(ob, ob);
    }

    public void remove(Vector3f position, ObjectWithStates ob){
        ConcurrentHashMap<ObjectWithStates, ObjectWithStates> set;
        Vector3f region = getRegionVector3f(position);
        if(this.vectorHashMap.containsKey(region)){
            set = this.vectorHashMap.get(region);
            set.remove(ob);
            if(set.isEmpty()){
                this.vectorHashMap.remove(region);
            }
        }
    }

    public void positionObjectWithStatesChanged(ObjectWithStates ob
                                            , Vector3f oldPossition
                                            , Vector3f newPosition){
        remove(oldPossition, ob);
        add(newPosition, ob);
    }


    /* INNER CLASS
     * CLASSES INTERNES
     */

    private class IteratorObjectWithStates implements Iterator<ObjectWithStates>{
        ConcurrentLinkedQueue<Iterator<ObjectWithStates>> queue = new ConcurrentLinkedQueue();

        public void addIterator(Iterator<ObjectWithStates> it){
            this.queue.offer(it);
        }



        public boolean hasNext() {
            return !this.queue.isEmpty();
        }

        public ObjectWithStates next() {
            ObjectWithStates ret = this.queue.peek().next();
            if(!this.queue.peek().hasNext()){
                this.queue.poll();
            }
            return ret;
        }

        public void remove() {
            queue = new ConcurrentLinkedQueue();
        }
    }

    private class IteratorLogicObjectWithStates implements Iterator<ObjectWithStates>{
        IteratorObjectWithStates iterator = new IteratorObjectWithStates();
        
        public void addIterator(Iterator<ObjectWithStates> it){
            this.iterator.addIterator(it);
        }

        public boolean hasNext() {
            return !this.iterator.hasNext();
        }

        public ObjectWithStates next() {
            ObjectWithStates ret = this.iterator.next();
            return ret;
        }

        public void remove() {
            this.iterator.remove();
        }
    }

    private class IteratorNodeObjectWithStates implements Iterator<Node>{
        IteratorObjectWithStates iterator = new IteratorObjectWithStates();

        public void addIterator(Iterator<ObjectWithStates> it){
            this.iterator.addIterator(it);
        }

        public boolean hasNext() {
            return !this.iterator.hasNext();
        }

        public Node next() {
            ObjectWithStates ret = this.iterator.next();
            return ret;
        }

        public void remove() {
            this.iterator.remove();
        }
    }

    /*
    private void createHashMap(){
        Vector3f vect;
        int cont=0;
        int contx=0;
        int conty=0;
        int contz=0;
        float sumx;
        float sumy;
        float sumz;
        float x=0;
        float y=0;
        float z=0;
        vect=new Vector3f(x, y, z);
        this.vectorHashMap.put(vect, cont);
        cont++;
        sumx= this.vectorLong.getX()/this.divisions;
        sumy= this.vectorLong.getY()/this.divisions;
        sumz= this.vectorLong.getZ()/this.divisions;

            while(contx<this.divisions-1){
                while(conty<this.divisions-1){
                    while(contz<this.divisions-1){
                        contz++;
                        z=z+sumz;
                        vect= new Vector3f(x, y, z);
                        this.vectorHashMap.put(vect,cont);
                        cont++;
                    }
                    z=0;
                    conty++;
                    y=y+sumy;
                    vect= new Vector3f(x, y, z);
                    this.vectorHashMap.put(vect,cont);
                    cont++;
                }
                y=0;
                contx++;
                x=x+sumx;
                vect = new Vector3f(x,y,z);
                this.vectorHashMap.put(vect,cont);
                cont++;
            }
    }
    // devolvemos la Region a partir de un vector
    public int getRegion(Vector3f vComp){

        float x;
        float y;
        float z;

        Vector3f vect;
        // Con esto intento conseguir la coordenada del vector
        x=  (this.vectorLong.getX()/this.divisions) * (vComp.getX() /(this.vectorLong.getX()/this.divisions)) - (vComp.getX() /(this.vectorLong.getX()/this.divisions)%10);
        y = (this.vectorLong.getX()/this.divisions) * (vComp.getY() /(this.vectorLong.getY()/this.divisions)) - (vComp.getY() /(this.vectorLong.getY()/this.divisions)%10);
        z = (this.vectorLong.getX()/this.divisions) * (vComp.getZ() /(this.vectorLong.getZ()/this.divisions)) - (vComp.getZ() /(this.vectorLong.getZ()/this.divisions)%10);
        vect = new Vector3f(x,y,z);
        return this.vectorHashMap.get(vect);
    }
     */


}
